package com.siki.provider.service.Impl;

import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.siki.provider.dto.contract.BuyCommodityListDTO;
import com.siki.provider.dto.purchase.GeneratePurchaseDTO;
import com.siki.provider.dto.purchase.PagePurchaseDTO;
import com.siki.provider.dto.purchase.UpdatePurchaseDTO;
import com.siki.provider.service.PurchaseService;
import com.siki.provider.vo.purchase.PurchaseCommodityVO;
import com.siki.provider.vo.purchase.PurchaseVO;
import com.siki.salessystemcommon.entity.Commodity;
import com.siki.salessystemcommon.entity.Contract;
import com.siki.salessystemcommon.entity.Invoice;
import com.siki.salessystemcommon.entity.Purchase;
import com.siki.salessystemcommon.entity.enumeration.ContractStatusEnum;
import com.siki.salessystemcommon.entity.enumeration.InvoiceStatusEnum;
import com.siki.salessystemcommon.entity.enumeration.PurchaseStatusEnum;
import com.siki.salessystemcommon.mapper.CommodityMapper;
import com.siki.salessystemcommon.mapper.ContractMapper;
import com.siki.salessystemcommon.mapper.InvoiceMapper;
import com.siki.salessystemcommon.mapper.PurchaseMapper;
import com.siki.salessystemcommon.utils.DecimalCalculation;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author Siki
 * @Date 2020/12/14
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class PurchaseServiceImpl implements PurchaseService {
    private final PurchaseMapper purchaseMapper;
    private final ContractMapper contractMapper;
    private final CommodityMapper commodityMapper;
    private final InvoiceMapper invoiceMapper;

    public PurchaseServiceImpl(PurchaseMapper purchaseMapper,
                               ContractMapper contractMapper,
                               CommodityMapper commodityMapper,
                               InvoiceMapper invoiceMapper) {
        this.purchaseMapper = purchaseMapper;
        this.contractMapper = contractMapper;
        this.commodityMapper = commodityMapper;
        this.invoiceMapper = invoiceMapper;
    }

    @Override
    public synchronized void generatePurchase(GeneratePurchaseDTO dto) {
        Purchase purchase = new Purchase();
        // 验证订购商品和未发货剩余商品数量
        List<BuyCommodityListDTO> purchaseCommodityList = dto.getPurchaseCommodityList();
        purchaseCommodityList.forEach(commodityListDTO -> {
            Map<String, Integer> commodityAmount = commodityMapper
                    .getContractCommodityAmount(dto.getContractId(), commodityListDTO.getId());
            int commodityNoSendAmount = commodityAmount.get("commodity_no_send_amount")
                    - Integer.parseInt(commodityListDTO.getCommodityAmount());
            Assert.isTrue(commodityNoSendAmount >= 0,
                    "采购数量不能大于合同未发货数量,出错商品:" +
                            getCommodity(commodityListDTO.getId()).getCommodityName());
            // 保存未发货数商品的数据(合同中原未发货数量-采购清单采购数量)
            commodityMapper.updateContractNoSeedCommodityAmount(dto.getContractId(),
                    commodityListDTO.getId(), commodityNoSendAmount);
        });
        // 采购清单总金额计算
        String commodityPrice = getCommodityPrice(purchaseCommodityList);
        // 采购清单信息保存
        purchase.setPurchaseStatus(PurchaseStatusEnum.UNPAID)
                .setContractId(dto.getContractId())
                .setEndingPoint(dto.getEndingPoint())
                .setStartingPoint(dto.getStartingPoint())
                .setPurchaseTotalMoney(commodityPrice)
                .setPurchasePaidMoney(dto.getCommodityPaidMoney());
        purchaseMapper.insert(purchase);
        // 保存采购清单与商品的关联数据
        purchaseMapper.insertPurchaseCommodity(Map.of(
                // java9提供的Map初始化方法,但这是不可变集合
                "pId", purchase.getId(),
                "comList", purchaseCommodityList
        ));
        //合同状态修改,生成采购清单,意味着已经确定下来了
        contractMapper.updateById(contractMapper
                .selectById(dto.getContractId())
                .setContractStatus(ContractStatusEnum.FULFILL));
    }

    @Override
    public synchronized void deletePurchase(Long id) {
        // 删除清单下的出货单
        var qw = new QueryWrapper<Invoice>();
        qw.eq("purchase_id", id);
        List<Invoice> invoices = invoiceMapper.selectList(qw);
        invoices.forEach(invoice -> {
            Assert.isTrue(invoice.getInvoiceStatus().equals(InvoiceStatusEnum.NO_PAID),
                    "清单下有存在已付款的出货单,不能删除");
            // 将出货单中的商品数量回滚到商品库存中
            Commodity commodity = getCommodity(invoice.getCommodityId());
            commodity.setCommodityInventory(commodity.getCommodityInventory()
                    + invoice.getCommodityAmount());
            commodityMapper.updateById(commodity);
        });
        // 删除清单业务流程
        Purchase purchase = getPurchaseById(id);
        // 采购单对应商品集合
        var commodityMap =
                commodityMapper.getPurchaseCommodityAmount(id);
        // 统计采购单中所有商品的信息(id和数量),用于退回合同中
        List<BuyCommodityListDTO> dtoList = new ArrayList<>();
        commodityMap.forEach(map -> {
            Map<String, Integer> commodityAmount = commodityMapper
                    .getContractCommodityAmount(purchase.getContractId(),
                            (Long) map.get("commodityId"));
            // 剩余的未发货商品
            Integer contractCommodityNoSendAmount = commodityAmount.get("commodity_no_send_amount");
            Integer amount = contractCommodityNoSendAmount + (Integer) map.get("commodityAmount");
            BuyCommodityListDTO buyCommodityListDTO = new BuyCommodityListDTO();
            buyCommodityListDTO.setId((Long) map.get("commodityId"));
            buyCommodityListDTO.setCommodityAmount("1");
            buyCommodityListDTO.setCommodityNoSendAmount(String.valueOf(amount));
            dtoList.add(buyCommodityListDTO);
        });
        // 将清单中的商品退回合同中未发货商品中
        contractMapper.updateContractAndCommodity(Map.of(
                "cid", purchase.getContractId(),
                "comList", dtoList
        ));

        // 删除清单和商品之间的关系
        purchaseMapper.deletePurchaseAndCommodity(id);
        purchaseMapper.deleteById(id);
        // 合同下清单数量<1时,修改合同状态为签订中
        if(purchaseMapper.selectList(new QueryWrapper<Purchase>()
                .eq("contract_id",purchase.getContractId()))
                .size()<1){
            //合同状态修改
            contractMapper.updateById(contractMapper
                    .selectById(purchase.getContractId())
                    .setContractStatus(ContractStatusEnum.SIGN));
        }
    }

    @Override
    public synchronized void updatePurchase(UpdatePurchaseDTO dto) {
        // 如果想更改的商品已生成出货单,则不能更改,要先将出货单删除
        var purchaseCommodityAmount =
                commodityMapper.getPurchaseCommodityAmount(dto.getId());
        // 获取清单
        Purchase purchase = getPurchaseById(dto.getId());
        // 获取清单下商品信息
        List<BuyCommodityListDTO> purchaseCommodityList = dto.getPurchaseCommodityList();
        purchaseCommodityList.forEach(commodityDTO -> {
            // 查出所有对应清单与商品的情况,找到指定商品,未生成出货单才能更改,否则不行
            purchaseCommodityAmount.forEach(map -> {
                if (map.get("commodityId").equals(commodityDTO.getId())) {
                    Assert.isTrue((int) map.get("shipped") == 0,
                            "被更改商品已生成出货单,无法更改,请现将出货单删除");
                }
            });
        });
        if (purchaseCommodityAmount.size() > 0) {
            // 更新清单与商品的信息
            List<BuyCommodityListDTO> dtoList = new ArrayList<>();
            List<Map<String, Object>> commodityInfoMapList = new ArrayList<>();
            // 拿到清单中所有商品
            purchaseCommodityAmount.stream()
                    .filter(map -> (int) map.get("shipped")!=1)
                    .forEach(map -> {
                Map<String, Integer> commodityAmount = commodityMapper
                        .getContractCommodityAmount(purchase.getContractId(),
                                (Long) map.get("commodityId"));
                // 双括号初始化
                commodityInfoMapList.add(new HashMap<>() {{
                    put("commodityId", map.get("commodityId"));
                    put("shippedStatus", map.get("shipped"));
                }});
                // 剩余的未发货商品
                Integer contractCommodityNoSendAmount = commodityAmount.get("commodity_no_send_amount");
                Integer amount = contractCommodityNoSendAmount + (Integer) map.get("commodityAmount");
                BuyCommodityListDTO buyCommodityListDTO = new BuyCommodityListDTO();
                buyCommodityListDTO.setId((Long) map.get("commodityId"));
                buyCommodityListDTO.setCommodityAmount("1");
                buyCommodityListDTO.setCommodityNoSendAmount(String.valueOf(amount));
                dtoList.add(buyCommodityListDTO);
            });

            // 将清单中的商品退回合同中未发货商品中
            contractMapper.updateContractAndCommodity(Map.of(
                    "cid", purchase.getContractId(),
                    "comList", dtoList
            ));
            // 删除清单和商品之间的关系
            purchaseMapper.deletePurchaseAndCommodity(purchase.getId());
            // 先插入现在的需求
            purchaseMapper.insertPurchaseCommodity(Map.of(
                    "pId", purchase.getId(),
                    "comList", purchaseCommodityList
            ));
            // 将现在需求的商品与数量持久化到数据库中
            purchaseCommodityList.forEach(commodityListDTO -> {
                commodityInfoMapList.forEach(map -> {
                    if (commodityListDTO.getId().equals(map.get("commodityId"))) {
                        map.put("commodityAmount", commodityListDTO.getCommodityAmount());
                        purchaseMapper.updatePurchaseAndCommodity(purchase.getId(), map);
                    }
                });
            });
            // 更改合同中的数据
            purchaseCommodityList.forEach(commodityListDTO -> {
                Map<String, Integer> commodityAmount = commodityMapper
                        .getContractCommodityAmount(purchase.getContractId(), commodityListDTO.getId());
                int commodityNoSendAmount = commodityAmount.get("commodity_no_send_amount")
                        - Integer.parseInt(commodityListDTO.getCommodityAmount());
                Assert.isTrue(commodityNoSendAmount >= 0,
                        "采购数量不能大于合同未发货数量,出错商品:" +
                                getCommodity(commodityListDTO.getId()).getCommodityName());
                // 保存未发货数商品的数据 = 合同中原未发货数量 - 采购清单采购数量
                commodityMapper.updateContractNoSeedCommodityAmount(purchase.getContractId(),
                        commodityListDTO.getId(), commodityNoSendAmount);
            });
        }
        // 采购清单总金额计算
        // TODO: 2020/12/29 @Siki: 有bug
        String commodityPrice = getCommodityPrice(purchaseCommodityList);
        purchase.setPurchaseTotalMoney(commodityPrice)
                .setStartingPoint(dto.getStartingPoint())
                .setEndingPoint(dto.getEndingPoint());
        purchaseMapper.updateById(purchase);

    }

    @Override
    public PurchaseVO getPurchase(Long id) {
        var purchaseCommodityAmount = commodityMapper.getPurchaseCommodityAmount(id);
        List<PurchaseCommodityVO> purchaseCommodityVOList = new ArrayList<>();
        // 将商品关系整合
        purchaseCommodityAmount.forEach(map -> {
            // 查询每个商品的数量,并整合成列表
            purchaseCommodityVOList.add(new PurchaseCommodityVO(
                    getCommodity((Long) map.get("commodityId")), map));
        });
        // 采购清单信息
        PurchaseVO purchaseVO = new PurchaseVO(getPurchaseById(id));
        purchaseVO.setPurchaseCommodityVOList(purchaseCommodityVOList);
        return purchaseVO;
    }

    @Override
    public IPage<PurchaseVO> getAllPurchase(PagePurchaseDTO dto) {

        QueryWrapper<Purchase> eq = new QueryWrapper<Purchase>()
                .eq("contract_id", dto.getContractId());
        if(StringUtils.isEmpty(dto.getPurchaseStatus())){
            eq.eq("purchase_status",dto.getPurchaseStatus());
        }
        return new Page<PurchaseVO>(dto.getPageNo(), dto.getPageSize())
                .setRecords(purchaseMapper.selectPage(new Page<>(dto.getPageNo(),
                                dto.getPageSize()),
                        eq)
                        .getRecords()
                        .stream().map(Purchase::getId)
                        .map(this::getPurchase)
                        .collect(Collectors.toList()));
    }


    /**
     * [私有方法] - 根据id查询采购清单
     *
     * @param id 采购清单id
     * @return Purchase采购清单
     */
    private Purchase getPurchaseById(Long id) {
        return Optional.ofNullable(purchaseMapper.selectById(id))
                .orElseThrow(() -> new RuntimeException("该id:" + id + "有误"));
    }

    /**
     * [私有方法] - 计算商品的总价
     *
     * @param dtoList 商品列表
     * @return 商品的总价
     */
    private String getCommodityPrice(List<BuyCommodityListDTO> dtoList) {
        String total = "0";
        for (BuyCommodityListDTO dto : dtoList) {
            total = DecimalCalculation.add(DecimalCalculation.mul(getCommodity(dto.getId())
                            .getCommodityPrice(),
                    dto.getCommodityAmount()), total);
        }
        return total;
    }

    /**
     * [私有方法] - 根据id查询商品
     *
     * @param id 商品id
     * @return Commodity 商品
     */
    private Commodity getCommodity(Long id) {
        return Optional.ofNullable(commodityMapper.selectById(id))
                .orElseThrow(() -> new RuntimeException("该id:" + id + "有误"));
    }
}
